package com.am.server.api.system.parameter.service.impl;

import cn.hutool.core.text.CharSequenceUtil;
import com.am.server.api.system.parameter.constant.ParameterConstant;
import com.am.server.api.system.parameter.dao.cache.ParameterCacheDao;
import com.am.server.api.system.parameter.dao.rdb.ParameterDao;
import com.am.server.api.system.parameter.exception.NotExistParameterException;
import com.am.server.api.system.parameter.model.dto.ParameterDto;
import com.am.server.api.system.parameter.model.dto.ParameterListQueryDto;
import com.am.server.api.system.parameter.model.dto.ParameterTreeDto;
import com.am.server.api.system.parameter.model.dto.UpdateParameterDto;
import com.am.server.api.system.parameter.model.entity.ParameterEntity;
import com.am.server.api.system.parameter.service.ParameterService;
import com.am.server.common.base.service.CommonService;
import com.google.common.collect.Lists;
import org.springframework.stereotype.Service;

import java.util.*;

/**
 * @author 阮雪峰
 */
@Service
public class ParameterServiceImpl implements ParameterService {

    private static final String PARAMETER_NOT_EXIST_MESSAGE_TEMPLATE = "参数【%s】不存在";

    private final ParameterCacheDao parameterCacheDao;
    private final ParameterDao parameterDao;
    private final CommonService commonService;

    public ParameterServiceImpl(ParameterCacheDao parameterCacheDao, ParameterDao parameterDao, CommonService commonService) {
        this.parameterCacheDao = parameterCacheDao;
        this.parameterDao = parameterDao;
        this.commonService = commonService;
    }

    @Override
    public List<ParameterTreeDto> treeList(ParameterListQueryDto query) {
        return buildTree(parameterDao.findAll(query));
    }

    @Override
    public void update(UpdateParameterDto dto) {
        parameterDao.findByUniqueKey(dto.getUniqueKey())
                .ifPresentOrElse(
                        parameterEntity -> {
                            parameterEntity.setValue(parameterEntity.getValue());
                            parameterDao.save(parameterEntity);
                            parameterCacheDao.save(parameterEntity);
                        },
                        () -> {
                            throw new NotExistParameterException(String.format(PARAMETER_NOT_EXIST_MESSAGE_TEMPLATE, dto.getUniqueKey()));
                        });
    }

    @Override
    public ParameterDto getByUniqueKey(String uniqueKey) {
        return parameterDao.findByUniqueKey(uniqueKey)
                .map(this::mapToVo)
                .orElseThrow(() -> new NotExistParameterException(String.format(PARAMETER_NOT_EXIST_MESSAGE_TEMPLATE, uniqueKey)));
    }

    @Override
    public List<ParameterDto> getByUniqueKeys(List<String> uniqueKeys) {
        return parameterDao.findByUniqueKeys(uniqueKeys)
                .stream()
                .map(this::mapToVo)
                .toList();
    }

    private ParameterDto mapToVo(ParameterEntity parameterEntity) {
        ParameterDto vo = new ParameterDto();
        vo.setName(parameterEntity.getName());
        vo.setUniqueKey(parameterEntity.getUniqueKey());
        vo.setValue(parameterEntity.getValue());
        return vo;
    }

    /**
     * 将所有的参数构建成树形结构
     *
     * @param parameters parameters
     * @return List<ParameterTreeVo>
     */
    private List<ParameterTreeDto> buildTree(List<ParameterEntity> parameters) {
        Set<String> rootCatlog = new TreeSet<>();
        Map<String, ParameterTreeDto> parameterMap = new HashMap<>(parameters.size() * 3);

        parameters.forEach(item -> {
            // 拆分目录
            List<String> catlogs = CharSequenceUtil.split(item.getCatlog(), ParameterConstant.CATLOG_DELIMITER);
            // 每层目录独立创建
            ParameterTreeDto latest = null;
            StringBuilder fullCatlog = new StringBuilder();
            for (int i = 0; i < catlogs.size(); i++) {
                String catlog = catlogs.get(i);
                fullCatlog.append(catlog);
                ParameterTreeDto obj;
                if (!parameterMap.containsKey(fullCatlog.toString())) {
                    obj = new ParameterTreeDto();
                    obj.setId(commonService.createNewId());
                    obj.setName(catlog);
                    obj.setLeaf(Boolean.FALSE);
                    obj.setChildren(Lists.newArrayList());
                    if (i > 0) {
                        latest.getChildren().add(obj);
                    }
                    parameterMap.put(fullCatlog.toString(), obj);
                } else {
                    obj = parameterMap.get(fullCatlog.toString());
                }

                if (i == 0) {
                    rootCatlog.add(catlog);
                }
                fullCatlog.append(ParameterConstant.CATLOG_DELIMITER);
                latest = obj;
            }

            ParameterTreeDto parameterTreeDto = new ParameterTreeDto();
            parameterTreeDto.setId(item.getId());
            parameterTreeDto.setName(item.getName());
            parameterTreeDto.setUniqueKey(item.getUniqueKey());
            parameterTreeDto.setValue(item.getValue());
            parameterTreeDto.setLeaf(Boolean.TRUE);
            parameterTreeDto.setType(item.getType());
            parameterTreeDto.setDelete(item.getDelete());
            assert latest != null;
            latest.getChildren().add(parameterTreeDto);
        });
        return rootCatlog.stream().map(parameterMap::get).toList();
    }

}
